class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }
class RaceCondition { private int value = 0; public void increment() { value++; // Race condition if not synchronized } public int getValue() { return value; } }
synchronized
keyword used for?synchronized
keyword ensures that only one thread can execute a method or block of code at a time, making it thread-safe.
class SyncExample { public synchronized void doSomething() { // Critical section } }
class SynchronizedBlock { private final Object lock = new Object(); public void methodWithBlock() { synchronized (lock) { // Critical section code } } }
class ThreadSafeCounter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }
Collections.synchronizedList()
or by using concurrent collections such as CopyOnWriteArrayList
.
Listlist = Collections.synchronizedList(new ArrayList<>()); list.add(1); // Thread-safe
class DeadlockExample { private final Object lock1 = new Object(); private final Object lock2 = new Object(); public void method1() { synchronized (lock1) { synchronized (lock2) { // Deadlock may occur here } } } public void method2() { synchronized (lock2) { synchronized (lock1) { // Deadlock may occur here } } } }
volatile
keyword in Java?volatile
keyword ensures that a variable's value is always read from and written to the main memory, preventing thread-specific caches from causing issues in multithreaded environments.
class VolatileExample { private volatile boolean flag = false; public void toggleFlag() { flag = !flag; } public boolean getFlag() { return flag; } }
ThreadLocal
class in Java?ThreadLocal
class provides thread-local variables, meaning each thread has its own independent copy of the variable, preventing synchronization issues.
ThreadLocalthreadLocalValue = ThreadLocal.withInitial(() -> 0); threadLocalValue.set(10); System.out.println(threadLocalValue.get()); // Each thread has its own value
synchronized
and ReentrantLock
?synchronized
is a built-in keyword for synchronization, while ReentrantLock
is a more flexible lock that allows for features like timed locks and interruptible lock acquisition.
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class LockExample { private final Lock lock = new ReentrantLock(); public void criticalSection() { lock.lock(); try { // Critical section code } finally { lock.unlock(); } } }
readWriteLock
and when would you use it?ReadWriteLock
allows multiple threads to read a resource concurrently while ensuring that only one thread can write to the resource at a time. It's useful when reads are more frequent than writes.
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; class ReadWriteLockExample { private final ReadWriteLock lock = new ReentrantReadWriteLock(); public void read() { lock.readLock().lock(); try { // Reading data } finally { lock.readLock().unlock(); } } public void write() { lock.writeLock().lock(); try { // Writing data } finally { lock.writeLock().unlock(); } } }
tryLock()
with timeouts, and keeping lock acquisitions as short as possible.
class DeadlockPrevention { private final Object lock1 = new Object(); private final Object lock2 = new Object(); public void method1() { synchronized (lock1) { synchronized (lock2) { // Safe to execute } } } }
ForkJoinPool
?ForkJoinPool
is a special implementation of the ExecutorService
designed for parallel tasks that can be broken down into smaller sub-tasks, making it ideal for divide-and-conquer algorithms.
import java.util.concurrent.RecursiveTask; import java.util.concurrent.ForkJoinPool; class ForkJoinExample extends RecursiveTask{ private final int threshold = 10; private final int[] array; private final int start, end; public ForkJoinExample(int[] array, int start, int end) { this.array = array; this.start = start; this.end = end; } @Override protected Integer compute() { if (end - start <= threshold) { int sum = 0; for (int i = start; i < end; i++) { sum += array[i]; } return sum; } else { int mid = (start + end) / 2; ForkJoinExample left = new ForkJoinExample(array, start, mid); ForkJoinExample right = new ForkJoinExample(array, mid, end); left.fork(); right.fork(); return left.join() + right.join(); } } public static void main(String[] args) { int[] array = new int[100]; for (int i = 0; i < 100; i++) array[i] = i; ForkJoinPool pool = new ForkJoinPool(); ForkJoinExample task = new ForkJoinExample(array, 0, array.length); int result = pool.invoke(task); System.out.println("Sum: " + result); } }
class ThreadContention { private int sharedResource = 0; public synchronized void increment() { sharedResource++; } public int getResource() { return sharedResource; } }
synchronized
and Atomic
variables?synchronized
blocks provide mutual exclusion for critical sections, while Atomic
variables offer lock-free, thread-safe operations for single variables.
import java.util.concurrent.atomic.AtomicInteger; class AtomicExample { private final AtomicInteger counter = new AtomicInteger(); public void increment() { counter.incrementAndGet(); } public int getCount() { return counter.get(); } }
synchronized
for the method that returns the instance or by using an enum for a thread-safe singleton.
class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
CountDownLatch
and how does it work?CountDownLatch
is a synchronization aid that allows one or more threads to wait until a set of operations in other threads completes. The latch is initialized with a count, and threads await until the count reaches zero.
import java.util.concurrent.CountDownLatch; class CountDownLatchExample { public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(2); Thread thread1 = new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } latch.countDown(); }); Thread thread2 = new Thread(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } latch.countDown(); }); thread1.start(); thread2.start(); latch.await(); // Waits for both threads to complete System.out.println("Both threads are done!"); } }
Semaphore
and how is it used?Semaphore
is a counting semaphore that controls access to a particular resource by multiple threads. It is initialized with a fixed number of permits, and threads can acquire or release permits to enter the critical section.
import java.util.concurrent.Semaphore; class SemaphoreExample { private static final Semaphore semaphore = new Semaphore(1); // One permit public void criticalSection() throws InterruptedException { semaphore.acquire(); try { // Critical section code } finally { semaphore.release(); } } }
volatile
keyword in Java, and how does it affect thread safety?volatile
keyword in Java ensures that a variable is directly read from and written to the main memory, making it visible to all threads. It provides a lighter alternative to synchronization but does not guarantee atomicity or consistency for compound operations.
class VolatileExample { private volatile boolean flag = false; public void changeFlag() { flag = true; } public boolean readFlag() { return flag; } }
wait()
, notify()
, and notifyAll()
?wait()
is used to pause the execution of a thread until it is notified, notify()
wakes up one thread waiting on the object’s monitor, and notifyAll()
wakes up all threads waiting on the object’s monitor.
class WaitNotifyExample { private final Object lock = new Object(); public void waitForSignal() throws InterruptedException { synchronized (lock) { lock.wait(); // Pauses thread } } public void sendSignal() { synchronized (lock) { lock.notify(); // Wakes up one waiting thread } } public void sendSignalToAll() { synchronized (lock) { lock.notifyAll(); // Wakes up all waiting threads } } }
ExecutorService
and how does it help with thread safety?ExecutorService
provides a higher-level replacement for manually creating threads. It manages a pool of threads and simplifies thread management by handling scheduling and execution, thus promoting better thread safety and resource management.
import java.util.concurrent.*; class ExecutorServiceExample { public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(2); executor.submit(() -> { // Task 1 System.out.println("Task 1 started"); }); executor.submit(() -> { // Task 2 System.out.println("Task 2 started"); }); executor.shutdown(); // Gracefully shuts down the executor } }
ReentrantReadWriteLock
and how does it differ from synchronized
blocks?ReentrantReadWriteLock
allows multiple readers or one writer at a time, making it more efficient than using synchronized
blocks when there are frequent read operations. It allows better concurrency by allowing multiple threads to read simultaneously.
import java.util.concurrent.locks.*; class ReentrantReadWriteLockExample { private final ReadWriteLock lock = new ReentrantReadWriteLock(); public void readData() { lock.readLock().lock(); try { // Read data } finally { lock.readLock().unlock(); } } public void writeData() { lock.writeLock().lock(); try { // Write data } finally { lock.writeLock().unlock(); } } }
AtomicInteger
or AtomicReference
provide atomic methods for variables.
import java.util.concurrent.atomic.AtomicInteger; class AtomicOperationsExample { private final AtomicInteger counter = new AtomicInteger(); public void increment() { counter.incrementAndGet(); // Atomic increment } public int getCount() { return counter.get(); // Atomic read } }
ThreadLocal
class and how does it improve thread safety?ThreadLocal
ensures that each thread has its own independent copy of a variable, preventing race conditions and synchronization issues. It provides a way to store thread-local data.
class ThreadLocalExample { private static final ThreadLocalthreadLocal = ThreadLocal.withInitial(() -> 1); public static void main(String[] args) { System.out.println(threadLocal.get()); // Prints 1 for the current thread threadLocal.set(2); System.out.println(threadLocal.get()); // Prints 2 for the current thread } }
class StarvationExample { private final Object lock = new Object(); public void task() { synchronized (lock) { // Critical section } } }
Thread.sleep()
and Object.wait()
?Thread.sleep()
causes the current thread to pause execution for a specified time, while Object.wait()
makes the current thread wait until it is notified, and it must be used within a synchronized block.
class SleepVsWait { public void sleepExample() throws InterruptedException { Thread.sleep(1000); // Pauses execution for 1 second } public void waitExample() throws InterruptedException { synchronized (this) { wait(1000); // Waits for notification or timeout } } }
synchronized
inside a method with a check to prevent unnecessary synchronization. It ensures thread safety while avoiding performance hits due to synchronization.
class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
class SynchronizedExample { private final Object lock = new Object(); public synchronized void synchronizedMethod() { // Code executed by only one thread at a time } public void synchronizedBlock() { synchronized (lock) { // Code executed by only one thread at a time } } }
synchronized
keyword to prevent race conditions in a Java program?synchronized
keyword ensures that only one thread can access the critical section of code at any given time, thus preventing race conditions.
class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }
ReentrantLock
and the synchronized
keyword?ReentrantLock
offers more advanced features compared to the synchronized
keyword, such as the ability to try locking with timeouts, interruptible locks, and fair locking, which ensures that threads acquire the lock in the order they requested it.
import java.util.concurrent.locks.*; class LockExample { private final Lock lock = new ReentrantLock(); public void lockExample() { lock.lock(); try { // Critical section code } finally { lock.unlock(); } } }
ReadWriteLock
interface, and when should it be used?ReadWriteLock
interface provides a mechanism for managing locks on data, allowing multiple threads to read the data concurrently while writing operations are exclusive. It is useful when the application has many read operations but fewer write operations.
import java.util.concurrent.locks.*; class ReadWriteLockExample { private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); public void readData() { rwLock.readLock().lock(); try { // Read operation } finally { rwLock.readLock().unlock(); } } public void writeData() { rwLock.writeLock().lock(); try { // Write operation } finally { rwLock.writeLock().unlock(); } } }
volatile
keyword in multithreaded programming?volatile
keyword ensures that a variable's value is always read from and written to the main memory, making it visible across all threads. However, it does not guarantee atomicity for compound operations.
class VolatileExample { private volatile boolean flag = false; public void changeFlag() { flag = true; } public boolean readFlag() { return flag; } }
java.util.concurrent
package, such as CopyOnWriteArrayList
, ConcurrentHashMap
, and BlockingQueue
. These collections handle synchronization internally, making them safe for use in multithreaded environments.
import java.util.concurrent.*; class ThreadSafeCollectionExample { private final ConcurrentMap map = new ConcurrentHashMap<>(); public void addData() { map.put(1, "Data"); } public String getData() { return map.get(1); } }
ThreadLocal
class in Java?ThreadLocal
class provides thread-local variables. Each thread accessing a ThreadLocal
variable has its own independent copy, making it thread-safe without synchronization.
class ThreadLocalExample { private static ThreadLocalthreadLocal = ThreadLocal.withInitial(() -> 1); public void increment() { threadLocal.set(threadLocal.get() + 1); } public int get() { return threadLocal.get(); } }
wait()
and sleep()
in Java?wait()
is used in synchronized methods to release the lock and allow other threads to acquire it. sleep()
pauses the thread for a specified amount of time without releasing any locks.
class WaitSleepExample { public synchronized void exampleWait() throws InterruptedException { wait(); // Releases the lock and waits } public void exampleSleep() throws InterruptedException { Thread.sleep(1000); // Pauses without releasing the lock } }
ReentrantReadWriteLock
class in Java?ReentrantReadWriteLock
class allows multiple threads to read shared data concurrently, but only one thread can write to the data at a time. It is useful when read operations are more frequent than write operations.
import java.util.concurrent.locks.*; class ReentrantReadWriteLockExample { private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); public void readData() { rwLock.readLock().lock(); try { // Read operation } finally { rwLock.readLock().unlock(); } } public void writeData() { rwLock.writeLock().lock(); try { // Write operation } finally { rwLock.writeLock().unlock(); } } }
ConcurrentHashMap
and HashMap
?ConcurrentHashMap
is thread-safe and allows multiple threads to read and write to the map concurrently without external synchronization. In contrast, HashMap
is not thread-safe and requires external synchronization to be used safely in a multithreaded environment.
import java.util.concurrent.*; class ConcurrentHashMapExample { private final ConcurrentMap map = new ConcurrentHashMap<>(); public void addData() { map.put(1, "Data"); } public String getData() { return map.get(1); } }
ExecutorService
help in managing thread safety?ExecutorService
provides a higher-level replacement for the traditional thread management techniques, like Thread
or Runnable
. It manages a pool of threads and allows for better control over concurrency and thread safety.
import java.util.concurrent.*; class ExecutorServiceExample { private final ExecutorService executor = Executors.newFixedThreadPool(10); public void executeTask(Runnable task) { executor.submit(task); } public void shutdown() { executor.shutdown(); } }
AtomicInteger
, AtomicLong
, and AtomicReference
to ensure thread safety without synchronization.
import java.util.concurrent.atomic.*; class AtomicExample { private final AtomicInteger counter = new AtomicInteger(0); public void increment() { counter.incrementAndGet(); } public int getCounter() { return counter.get(); } }
ForkJoinPool
, and when is it useful?ForkJoinPool
is designed for parallel tasks that can be split into smaller tasks. It is useful for divide-and-conquer algorithms, where tasks can be recursively split and then combined.
import java.util.concurrent.*; class ForkJoinPoolExample { private final ForkJoinPool pool = new ForkJoinPool(); public void processTask(RecursiveTasktask) { pool.invoke(task); } }
CountDownLatch
in Java, and how does it work?CountDownLatch
is used to synchronize threads by allowing one or more threads to wait until a set of operations performed by other threads reaches a specified count.
import java.util.concurrent.*; class CountDownLatchExample { private final CountDownLatch latch = new CountDownLatch(3); public void task() throws InterruptedException { // Perform some task latch.countDown(); // Decrements the latch count } public void await() throws InterruptedException { latch.await(); // Waits until the count reaches zero } }
synchronized
block and synchronized
method?synchronized
method locks the instance of the class or the class object (if it's static) for the duration of the method's execution. A synchronized
block allows you to lock a specific object, offering finer control over which resource is locked.
class SynchronizedExample { private final Object lock = new Object(); public synchronized void method() { // synchronized method } public void block() { synchronized (lock) { // synchronized block } } }
volatile
keyword used for in Java?volatile
keyword is used to indicate that a variable's value may be changed by multiple threads. It ensures visibility of changes made to the variable across all threads, but it doesn't guarantee atomicity.
class VolatileExample { private volatile boolean flag = false; public void setFlagTrue() { flag = true; } public boolean getFlag() { return flag; } }
ReentrantLock
, and how does it differ from synchronized
?ReentrantLock
is a more flexible and feature-rich alternative to the synchronized keyword. It provides additional functionality like lock acquisition with a timeout and the ability to interrupt threads waiting for a lock.
import java.util.concurrent.locks.*; class ReentrantLockExample { private final ReentrantLock lock = new ReentrantLock(); public void exampleMethod() { lock.lock(); try { // critical section } finally { lock.unlock(); } } }
Lock
in Java, and when should it be used?Lock
interface provides a more sophisticated and flexible locking mechanism compared to the synchronized keyword. It should be used when you need fine-grained control over locks or need to handle lock timeouts, retries, etc.
import java.util.concurrent.locks.*; class LockExample { private final Lock lock = new ReentrantLock(); public void exampleMethod() { lock.lock(); try { // critical section } finally { lock.unlock(); } } }
AtomicInteger
class ensure thread safety?AtomicInteger
class ensures thread safety by providing atomic operations, meaning operations like increment and decrement are guaranteed to complete in a single, indivisible step, without the need for explicit synchronization.
import java.util.concurrent.atomic.*; class AtomicIntegerExample { private final AtomicInteger counter = new AtomicInteger(); public void increment() { counter.incrementAndGet(); } public int getCounter() { return counter.get(); } }
Semaphore
in Java, and how is it used?Semaphore
controls access to a shared resource by limiting the number of threads that can access it simultaneously. It uses permits to manage the number of threads allowed to execute a particular section of code.
import java.util.concurrent.*; class SemaphoreExample { private final Semaphore semaphore = new Semaphore(2); public void accessResource() throws InterruptedException { semaphore.acquire(); try { // Access the shared resource } finally { semaphore.release(); } } }
CountDownLatch
class?CountDownLatch
is used to block a thread until a specified number of events occur. It is often used to wait for a collection of threads to complete their tasks before proceeding.
import java.util.concurrent.*; class CountDownLatchExample { private final CountDownLatch latch = new CountDownLatch(3); public void task() throws InterruptedException { // Perform task latch.countDown(); } public void waitForCompletion() throws InterruptedException { latch.await(); } }
ReentrantLock
with fairness enabled or using ExecutorService
with a proper thread pool. synchronized
methods/blocks, locks, or thread-safe collections.
ReadWriteLock
and when should it be used?ReadWriteLock
allows multiple threads to read a resource concurrently, but only one thread can write to it at a time. It's ideal for scenarios where reads are frequent and writes are rare.
import java.util.concurrent.locks.*; class ReadWriteLockExample { private final ReadWriteLock lock = new ReentrantReadWriteLock(); private int sharedResource = 0; public void read() { lock.readLock().lock(); try { // read resource } finally { lock.readLock().unlock(); } } public void write(int value) { lock.writeLock().lock(); try { sharedResource = value; } finally { lock.writeLock().unlock(); } } }
wait()
and sleep()
?wait()
is used to pause the execution of a thread and release the lock until another thread signals it to continue, while sleep()
pauses the execution of a thread for a fixed period but does not release any locks.
class WaitSleepExample { private final Object lock = new Object(); public void exampleWait() throws InterruptedException { synchronized (lock) { lock.wait(); } } public void exampleSleep() throws InterruptedException { Thread.sleep(1000); } }
ThreadLocal
class in Java?ThreadLocal
provides thread-local variables. Each thread accessing a ThreadLocal
variable has its own independent copy, making it thread-safe without synchronization.
class ThreadLocalExample { private static final ThreadLocalthreadLocal = ThreadLocal.withInitial(() -> 0); public void increment() { threadLocal.set(threadLocal.get() + 1); } public int getValue() { return threadLocal.get(); } }
Deadlock
, and how can it be prevented?Deadlock
occurs when two or more threads are blocked forever because they are waiting for each other to release a resource. It can be prevented by using a proper locking order or implementing a timeout mechanism. ExecutorService
provide thread pool management?ExecutorService
provides an easy way to manage a pool of threads, allowing for asynchronous task execution. It handles thread creation, execution, and termination without the need to manually manage individual threads.
import java.util.concurrent.*; class ExecutorServiceExample { private final ExecutorService executorService = Executors.newFixedThreadPool(2); public void submitTask() { executorService.submit(() -> { // task logic }); } public void shutdown() { executorService.shutdown(); } }
ThreadPoolExecutor
and when would you use it?ThreadPoolExecutor
is a customizable implementation of the ExecutorService
interface. It allows fine-grained control over the thread pool, such as core pool size, maximum pool size, and queue type.
import java.util.concurrent.*; class ThreadPoolExecutorExample { private final ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); public void submitTask() { executor.submit(() -> { // task logic }); } public void shutdown() { executor.shutdown(); } }
synchronized
and Lock
?synchronized
is a simpler mechanism that automatically acquires and releases locks, while Lock
provides more flexibility, allowing for manual lock acquisition, timed lock waits, and lock interruption. ForkJoinPool
in Java?ForkJoinPool
is a special type of thread pool designed for parallel computing. It allows tasks to be recursively split (forked) into smaller tasks and then joined back together when completed.
import java.util.concurrent.*; class ForkJoinPoolExample { private final ForkJoinPool forkJoinPool = new ForkJoinPool(); public void executeTask() { forkJoinPool.submit(() -> { // task logic }); } public void shutdown() { forkJoinPool.shutdown(); } }
CompletableFuture
in Java and how does it relate to thread safety?CompletableFuture
is a class that allows you to write asynchronous, non-blocking code. It provides a way to run tasks asynchronously and then combine or process their results.
import java.util.concurrent.*; class CompletableFutureExample { public void runAsyncTask() { CompletableFuture.supplyAsync(() -> { return "Hello"; }).thenAccept(result -> { // process result }); } }
volatile
keyword in Java and how does it affect thread safety?volatile
keyword ensures that a variable's value is always read from and written to the main memory, not cached in local thread memory. This ensures visibility across threads but does not provide atomicity.
class VolatileExample { private volatile boolean flag = false; public void toggleFlag() { flag = !flag; } public boolean getFlag() { return flag; } }
synchronized
block in Java, and when is it preferred over method synchronization?synchronized
block allows for fine-grained control over which part of the method needs synchronization. It's preferred when only a portion of the method requires synchronization, minimizing the locked section.
class SynchronizedBlockExample { private final Object lock = new Object(); public void criticalSection() { synchronized (lock) { // Critical section code } } }
CountDownLatch
and how can it be used for thread synchronization?CountDownLatch
allows one or more threads to wait until a set of operations in other threads are completed. It’s commonly used to wait for a certain condition before proceeding.
import java.util.concurrent.*; class CountDownLatchExample { private final CountDownLatch latch = new CountDownLatch(1); public void waitForCompletion() throws InterruptedException { latch.await(); System.out.println("Operation completed!"); } public void signalCompletion() { latch.countDown(); } }
Semaphore
in Java, and how does it regulate access to resources?Semaphore
controls access to a shared resource by maintaining a set number of permits. Threads can acquire permits to access the resource and must release the permit when done.
import java.util.concurrent.*; class SemaphoreExample { private final Semaphore semaphore = new Semaphore(3); public void accessResource() throws InterruptedException { semaphore.acquire(); try { // Access shared resource } finally { semaphore.release(); } } }
ReentrantLock
, and how is it different from using synchronized
?ReentrantLock
is a more flexible lock that provides features such as try-lock, timed lock, and interruptible lock acquisition, unlike synchronized
, which is simpler but less feature-rich.
import java.util.concurrent.locks.*; class ReentrantLockExample { private final ReentrantLock lock = new ReentrantLock(); public void safeMethod() { lock.lock(); try { // Critical section code } finally { lock.unlock(); } } }
ReentrantReadWriteLock
improve thread safety in read-heavy applications?ReentrantReadWriteLock
allows multiple threads to read a resource concurrently but ensures exclusive access when writing. This improves thread safety and performance in applications where reads are much more frequent than writes.
import java.util.concurrent.locks.*; class ReentrantReadWriteLockExample { private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private int resource; public int read() { rwLock.readLock().lock(); try { return resource; } finally { rwLock.readLock().unlock(); } } public void write(int value) { rwLock.writeLock().lock(); try { resource = value; } finally { rwLock.writeLock().unlock(); } } }
ForkJoinTask
and how does it relate to parallel computation in Java?ForkJoinTask
represents tasks in a ForkJoinPool
. It's designed for tasks that can be recursively split into smaller sub-tasks, which can then be executed in parallel and combined.
import java.util.concurrent.*; class ForkJoinTaskExample { private final ForkJoinPool forkJoinPool = new ForkJoinPool(); public void executeTask() { forkJoinPool.submit(() -> { // Task logic }); } public void shutdown() { forkJoinPool.shutdown(); } }
AtomicInteger
and other atomic classes help with thread safety?AtomicInteger
and other atomic classes provide a thread-safe way to perform operations on variables like incrementing or updating values. These classes ensure atomicity without the need for explicit synchronization.
import java.util.concurrent.atomic.*; class AtomicIntegerExample { private final AtomicInteger counter = new AtomicInteger(0); public void increment() { counter.incrementAndGet(); } public int getValue() { return counter.get(); } }
ThreadFactory
interface and how can it help manage threads in Java?ThreadFactory
is an interface used to create new threads with customized properties (such as name, priority, etc.), often used in thread pools for better thread management and diagnostics.
import java.util.concurrent.*; class CustomThreadFactory implements ThreadFactory { private int threadCount = 0; @Override public Thread newThread(Runnable r) { return new Thread(r, "CustomThread-" + threadCount++); } }
ThreadLocal
class in Java?ThreadLocal
class provides thread-local variables. Each thread accessing a ThreadLocal
variable has its own independent copy, preventing conflicts between threads.
class ThreadLocalExample { private ThreadLocalthreadLocalValue = ThreadLocal.withInitial(() -> 0); public void increment() { threadLocalValue.set(threadLocalValue.get() + 1); } public int getValue() { return threadLocalValue.get(); } }
Deadlock
in Java and how can it be avoided?class DeadlockExample { private final Object lock1 = new Object(); private final Object lock2 = new Object(); public void method1() { synchronized (lock1) { synchronized (lock2) { // Do something } } } public void method2() { synchronized (lock2) { synchronized (lock1) { // Do something } } } }
ReentrantLock
support interruptible lock acquisition?ReentrantLock
allows for interruptible lock acquisition using the lockInterruptibly()
method, which allows a thread to acquire a lock unless interrupted.
import java.util.concurrent.locks.*; class ReentrantLockInterruptibleExample { private final ReentrantLock lock = new ReentrantLock(); public void executeWithLock() throws InterruptedException { lock.lockInterruptibly(); try { // Critical section code } finally { lock.unlock(); } } }
ForkJoinPool
and how does it optimize parallel execution?ForkJoinPool
is a specialized thread pool designed for parallel computing. It efficiently manages tasks that can be recursively divided into smaller sub-tasks, allowing better resource utilization.
import java.util.concurrent.*; class ForkJoinPoolExample { private final ForkJoinPool forkJoinPool = new ForkJoinPool(); public void submitTask() { forkJoinPool.submit(() -> { // Task logic }); } public void shutdown() { forkJoinPool.shutdown(); } }
AtomicReference
in Java?AtomicReference
provides a thread-safe way to update reference types (like objects) atomically, without using locks or synchronization, improving performance and reducing contention.
import java.util.concurrent.atomic.*; class AtomicReferenceExample { private final AtomicReferenceref = new AtomicReference<>("Initial"); public void updateReference() { ref.set("Updated"); } public String getReference() { return ref.get(); } }
Executors.newFixedThreadPool()
in managing thread pools?Executors.newFixedThreadPool()
creates a thread pool with a fixed number of threads. It is useful when you want to limit the number of concurrent threads to manage system resources efficiently.
import java.util.concurrent.*; class FixedThreadPoolExample { private final ExecutorService executorService = Executors.newFixedThreadPool(3); public void submitTasks() { for (int i = 0; i < 5; i++) { executorService.submit(() -> { // Task logic }); } } public void shutdown() { executorService.shutdown(); } }
Callable
differ from Runnable
in Java?Callable
is similar to Runnable
, but it can return a result and throw exceptions. It is often used in parallel computing tasks that need to return data or handle errors.
import java.util.concurrent.*; class CallableExample implements Callable{ @Override public String call() throws Exception { return "Result from callable"; } } class ExecutorExample { private final ExecutorService executorService = Executors.newCachedThreadPool(); public void submitCallable() throws InterruptedException, ExecutionException { Future future = executorService.submit(new CallableExample()); System.out.println(future.get()); } }
Thread.sleep()
in thread synchronization?Thread.sleep()
pauses the current thread for a specified amount of time. It is not a synchronization mechanism but can be used to simulate delays in thread execution.
class SleepExample { public void simulateDelay() throws InterruptedException { Thread.sleep(1000); // Sleep for 1 second } }
Object.wait()
method, and how is it used for inter-thread communication?Object.wait()
is used to make the current thread release the lock and enter the waiting state until notified by another thread. It is often used for communication between threads in synchronization blocks.
class WaitNotifyExample { private final Object lock = new Object(); public void awaitThread() throws InterruptedException { synchronized (lock) { lock.wait(); } } public void notifyThread() { synchronized (lock) { lock.notify(); } } }
volatile
keyword used for in Java?volatile
keyword is used to indicate that a variable may be modified by multiple threads. It ensures that the value of the variable is always read from and written to the main memory, preventing caching issues.
class VolatileExample { private volatile boolean flag = false; public void toggleFlag() { flag = !flag; } public boolean getFlag() { return flag; } }
synchronized
methods and blocks in Java?synchronized
method ensures that only one thread can execute that method at a time. A synchronized
block, on the other hand, only synchronizes a specific section of code within a method, offering more fine-grained control over synchronization.
class SynchronizedMethodsBlocksExample { private int counter = 0; // Synchronized method public synchronized void incrementCounter() { counter++; } // Synchronized block public void incrementCounterWithBlock() { synchronized (this) { counter++; } } }
CountDownLatch
and CyclicBarrier
differ in Java?CountDownLatch
is used to block a thread until a specified count reaches zero, usually for waiting for multiple threads to finish. A CyclicBarrier
is used to synchronize a fixed number of threads, allowing them to all start together at a barrier point and then continue executing.
import java.util.concurrent.*; class CountDownLatchExample { private final CountDownLatch latch = new CountDownLatch(3); public void waitForThreads() throws InterruptedException { latch.await(); // Wait for countdown to reach 0 } public void threadDone() { latch.countDown(); // Decreases the latch count by 1 } } class CyclicBarrierExample { private final CyclicBarrier barrier = new CyclicBarrier(3); public void waitAtBarrier() throws InterruptedException, BrokenBarrierException { barrier.await(); // Threads wait at the barrier } }
Thread.sleep()
and Object.wait()
in Java?Thread.sleep()
pauses the current thread for a specified time, while Object.wait()
releases the lock on the object and allows other threads to acquire the lock, waiting until another thread notifies it.
class SleepWaitExample { private final Object lock = new Object(); public void sleepExample() throws InterruptedException { Thread.sleep(1000); // Sleeps for 1 second } public void waitExample() throws InterruptedException { synchronized (lock) { lock.wait(); // Wait for notification from another thread } } }
ExecutorService
simplify thread management in Java?ExecutorService
manages a pool of threads for you, simplifying the task of thread creation, management, and shutdown. It reduces the complexity of directly managing threads and improves performance by reusing thread resources.
import java.util.concurrent.*; class ExecutorServiceExample { private final ExecutorService executorService = Executors.newFixedThreadPool(3); public void submitTask() { executorService.submit(() -> { // Task logic }); } public void shutdown() { executorService.shutdown(); } }
synchronized
keyword and ReentrantLock
in Java?synchronized
keyword is simpler and provides automatic lock release. ReentrantLock
is more flexible and offers additional features like timed lock acquisition, interruptible lock acquisition, and ability to try locking.
import java.util.concurrent.locks.*; class ReentrantLockExample { private final ReentrantLock lock = new ReentrantLock(); public void execute() { lock.lock(); try { // Critical section } finally { lock.unlock(); } } }
Thread.interrupt()
method in Java?Thread.interrupt()
method is used to interrupt a thread's execution. It sets the thread's interrupt flag, which can be checked by the thread to decide if it should terminate its task early.
class InterruptExample { public void longRunningTask() throws InterruptedException { while (!Thread.currentThread().isInterrupted()) { // Simulate a long-running task } } public void interruptThread(Thread thread) { thread.interrupt(); } }
Semaphore
in Java and when would you use it?Semaphore
is a synchronization aid that controls access to a shared resource by multiple threads. It allows a set number of threads to access a resource at a time and blocks other threads until a permit is available.
import java.util.concurrent.*; class SemaphoreExample { private final Semaphore semaphore = new Semaphore(3); public void accessResource() throws InterruptedException { semaphore.acquire(); // Acquire a permit try { // Access shared resource } finally { semaphore.release(); // Release the permit } } }
AtomicInteger
work and why is it useful in multi-threaded environments?AtomicInteger
provides atomic operations for integers. It ensures that the value is updated safely across threads without the need for synchronization.
import java.util.concurrent.atomic.*; class AtomicIntegerExample { private final AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); // Atomically increments the value } public int getCount() { return count.get(); } }